function xDef()
{
  for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])=='undefined') return false;}
  return true;
}

function SniffyURLMatch (match) {
	var _protocol = (xDef(match.protocol) ? match.protocol : 'http');
	var _domain = match.domain;
	var _topDomain = match.topDomain;
	var _file = (xDef(match.file) ? match.file : '/');
	var _path = _file.split('/');
	var _query = match.query;
	var _spec = (xDef(match.spec) ? match.spec : '');
	var _postfix = (xDef(match.postfix) ? match.postfix : '');
	var __query = (match._query != '' ? match._query: undefined );
	this.__index = match._index;
	this.__len = match._len;
	var _u = _domain +
		(_file != '/' ? 
			_file +	(xDef(__query) ? "?" + __query : "") :
			(xDef(__query) ? "/?" + __query : "")
		);
	var _full = _protocol + "://" + _u;
	var _ext = /(\.([a-z]+))$/i
	_ext = _ext.exec(_file);
	if (_ext != null)
		_ext = _ext[2];
	else
		_ext = '';
	
	this.topDomain = function(domain) {
		return (_topDomain == domain);
	}
	
	this.hasParam = function(pname, valueMatch) {
		if (xDef(_query[pname])) {
			if (xDef(valueMatch)) {
				return (valueMatch.exec(_query[pname]) != null);
			} else {
				return true;
			}
		} else {
			return false;
		}
	}
	
	this.param = function (pname) {
		if (this.hasParam(pname)) {
			return _query[pname];
		} else {
			throw pname + ' not in the parametes list of the URL';
			return null;
		}
	}
	
	this.hasType = function () {
		if (_ext != null) {
			for(var i=0; i<arguments.length; ++i){
				if(arguments[i].toLowerCase() == _ext.toLowerCase()) return true;
			}
		}
		return false;
	}
	
	this.specifier = function (spec) {
		if (xDef(_spec) && _spec == spec) {
			return true;
		} else {
			return false;
		}
	}
	
	this.postfix = function (postfix) {
		if (xDef(_postfix) && _postfix == postfix) {
			return true;
		} else {
			return false;
		}		
	}	
	
	this.getURL = function() {
		return _full;
	}
	
	this.displayURL = function() {
		return _u;
	}
	
	this.getPath = function(i) {
		if (xDef(i)) return _path[i]
		else return _path;
	}
	
	this.matchRegExpPath = function (exp) {
		return exp.exec(_file) != null;
	}
	
	this.domain = function (domain) {
		return _domain == domain;
	}
}

var Sniffy = function() {
	
	var checkers = Array();
	
	var maxWidth = 440;
	
	function parseQueryString(data) {
		if (!xDef(data) || data==null) data = '';
		var ret = new Array()
		pairs = data.split('&');
		for (idx in pairs) {
			pair = pairs[idx].split('=');
			ret[pair[0]] = pair[1];
		}
		return ret;
	}
	
	function parse(data) {
		var result="";
		if (typeof data != 'string') {
			throw "Invalid argument for Sniffy.parse(). Expected string, got "+typeof(text)+".";
		}

		var expression = new RegExp;
		var matches = new Array();
		expression = /(^|\s)((url|audio|video|image|)\s+)?((http|https):\/\/)?(([a-zA-Z0-9\-\_]+\.)+([A-Za-z]{2,4}))(\/([^\s\?$]*))?(\?([^\s\?$]+))?(\s+(image|audio|video)(\s|$))?/gm;
		
		var i = 0;
		
		while (match = expression.exec(data)) {
			matches[i] = new SniffyURLMatch({
				'spec': match[3],
				'protocol': match[5],
				'domain': match[6],
				'topDomain': match[7] + match[8],
 				'file' : match[9],
 				'query': parseQueryString(match[12]),
 				'postfix': match[14],
				'_query' : match[12],
				'_index' : (match[0][0] == ' ' ? match.index + 1 : match.index),
				'_len': (match[0][0] == ' ' ? match[0].length - 1 : match[0].length)
			});
			i++;
		}

		if (!i) return data;
		
		var index = 0;
		for (i in matches) {
			var catched = false;
			var transformation = false;
			for (idx in checkers) {
				transformation = checkers[idx](matches[i]);
				if (transformation != false) {
					catched = true;
					break;
				}
			}
			if (!catched) {
				transformation = url(matches[i]);
			}
			result += data.substr(index,matches[i].__index - index) + transformation;
			index += matches[i].__index - index + matches[i].__len;
		}
		result += data.substr(index);
		return result;
	}
	

	function addChecker(checker) {
		if (typeof checker == 'function'){
			checkers.push(checker);
		} else throw "Invalid argument for Sniffy.addChecker. Exprected function(match), got "+typeof(checker)+".";
	}
	
	function flashObject(src, vars, width, height, bgcolor, wmode, scriptAccess, allowFullscreen) {
		vars = (xDef(vars)?vars:{});
		width = (xDef(width)?width:400);
		height = (xDef(height)?height:300);
		bgcolor = (xDef(bgcolor)?bgcolor:'#FFFFFF');
		wmode = (xDef(wmode)?wmode:'window');
		scriptAccess = (xDef(scriptAccess)?scriptAccess:'sameDomain');
		allowFullscreen = (xDef(allowFullscreen)?allowFullscreen:true);
		
		var obj = '';
		
		if (width > maxWidth) {
			k = maxWidth/width;
			width = maxWidth;
			height = k * height;
		}
		
		obj='<object width="'+width+'" height="'+height+'">';
		obj+='<param name="movie" value="'+src+'"></param>';
		obj+='<param name="allowFullscreen" value="'+allowFullscreen+'"></param>';
		obj+='<param name="wmode" value="'+allowFullscreen+'"></param>';		
		obj+='<param name="allowScriptAccess" value="'+scriptAccess+'"></param>';		
		obj+='<embed src="'+src+'" ' +
				'type="application/x-shockwave-flash" allowfullscreen="'+allowFullscreen+'" ' +
				'width="'+width+'" height="'+height+'" ' +
				'allowScriptAccess="'+scriptAccess+'" wmode="'+wmode+'" >' +
			'</embed>';
		obj+='</object>';
		
		return obj;		
	}
	
	function url(match) {
		return '<a href="'+match.getURL()+'">'+match.displayURL()+'</a>'; 
	}

	function escaped_url(match) {
		if (match.specifier('url')) {
			return url(match);
		} else {
			return false;
		}
	}
	
	function youtube(match) {
		if (match.topDomain('youtube.com') && match.hasParam('v',/^[A-Z0-9\_\-]+$/i)) {
			return flashObject('http://www.youtube.com/v/'+match.param('v'),{},425,344);
		} else {
			return false;
		}
	}
	
	function image(match) {
		if (match.hasType('jpg','png','gif')) {
			return '<a href="'+match.getURL()+'"><img src="'+match.getURL()+'" alt="'+match.displayURL()+'" style="max-width:'+maxWidth+'px"></a>';
		} else {
			return false;
		}
	}
	
	function trilulilu_video(match) {
		if (match.topDomain('trilulilu.ro') 
			&& (match.specifier('video') || match.specifier(''))
			&& match.matchRegExpPath(/\/[a-z0-9\_\-\.]+\/[a-z0-9\_\-]+/i)) {
			return flashObject(
				'http://www.trilulilu.ro/embed/flash.php?type=video&hash='+match.getPath(2)+'&userid='+match.getPath(1),
				{},
				440,
				362
			);
		} else {
			return false;
		}
	}

	function trilulilu_audio(match) {
		if (match.topDomain('trilulilu.ro') 
			&& match.specifier('audio')
			&& match.matchRegExpPath(/\/[a-z0-9\_\-\.]+\/[a-z0-9\_\-]+/i)) {
			return flashObject(
				'http://www.trilulilu.ro/embed/flash.php?type=audio&hash='+match.getPath(2)+'&userid='+match.getPath(1),
				{},
				440,
				41
			);
		} else {
			return false;
		}
	}

	function trilulilu_image(match) {
		if (match.topDomain('trilulilu.ro') 
			&& match.specifier('image')
			&& match.matchRegExpPath(/\/[a-z0-9\_\-\.]+\/[a-z0-9\_\-]+/i)
			) {
			return flashObject(
				'http://www.trilulilu.ro/embed/flash.php?type=image&hash='+match.getPath(2)+'&userid='+match.getPath(1),
				{},
				440,
				362
			);
		} else {
			return false;
		}
	}
	
	function google_maps(match) {
		if (match.domain('maps.google.com') && 
			match.hasParam('ll') && 
			match.hasParam('spn') && 
			match.hasParam('t') && 
			match.hasParam('z')) {
		return '<iframe width="425" height="350" ' +
				'frameborder="0" scrolling="no" marginheight="0" marginwidth="0" ' +
				'src="http://maps.google.com/?ie=UTF8&amp;' +
				'll='+match.param('ll')+'&amp;' +
				'spn='+match.param('spn')+'&amp;' +
				't='+match.param('t')+'&amp;z='+match.param('z')+'&amp;' +
				'output=embed">' +
				'</iframe><br />' +
				'<small>' +
				'<a href="http://maps.google.com/?ie=UTF8&amp;ll=45.755008,21.232796&amp;spn=0.014612,0.038581&amp;t=k&amp;z=15&amp;source=embed" ' +
				'style="color:#0000FF;text-align:left">View Larger Map</a></small>';
		} else {
			return false;
		}
	}
		
	function parseDOMTree(e) {
		if (xDef(e.nodeName) && e.nodeName.toLowerCase() != 'a') {
			var i;
			var node = null;
			for (i = 0; i < e.childNodes.length; i++) {
				var j;
				if (e.childNodes[i].nodeName == '#text') {
					parsed = parse(e.childNodes[i].nodeValue);
					if (parsed != e.childNodes[i].nodeValue) {
						node = document.createElement('span');
						node.className = 'sniffy-parsed';
						node.innerHTML = parsed; 
						targetNode = e.childNodes[i];
						e.replaceChild(node,targetNode);
					}
				} else {
					parseDOMTree(e.childNodes[i]);
				}
			}
		}
	}
	
	function getMaxWidth() {
		return maxWidth;
	}
	
	function setMaxWidth(newMaxWidth) {
		maxWidth = newMaxWidth;
	}

	addChecker(escaped_url);
	addChecker(youtube);
	addChecker(image);
	addChecker(trilulilu_audio);
	addChecker(trilulilu_image);
	addChecker(trilulilu_video);
	
	return {
		'parse' : parse,
		'parseDOMTree': parseDOMTree,
		'addChecker' : addChecker,
		'getMaxSuggestedWidth' : getMaxWidth,
		'setMaxSuggestedWidth' : setMaxWidth
	}
} ();
